package com.hy.lightning.boot.core;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.hy.lightning.boot.annotation.RespType;
import com.hy.lightning.commons.utils.ObjectUtil;

public class ServicesFactory {

	public static Map<String,ServiceConfiguration> servicesCache = new ConcurrentHashMap<>();
	
	private static Logger logger = Logger.getLogger(ServicesFactory.class);
	
	public static void loadService(List<ServiceConfiguration> services) {
		if(services == null) return;
		for(ServiceConfiguration serviceConfiguration: services) {
			String name = serviceConfiguration.getName();
			servicesCache.put(name, serviceConfiguration);
		}
		logger.info("servicesCache 加载完成，服务数量:"+servicesCache.size()+"," + servicesCache);
	}
	
	public static void loadService(String ...servicesXmlPaths) throws DocumentException, ClassNotFoundException {
		if(servicesXmlPaths == null) return;
		SAXReader reader = new SAXReader();
		List<ServiceConfiguration> services = new ArrayList<>();
		for(String servicesXmlPath : servicesXmlPaths) {
			Document document = reader.read(new File(servicesXmlPath));
			Element root = document.getRootElement();
			List<?> elements = root.elements(ROOT_NAME);
			for(Iterator<?> it = elements.iterator(); it.hasNext();) {
				Element e = (Element) it.next();
				String name = e.attributeValue(ServiceConfiguration.NAME);
				String classPath = e.attributeValue(ServiceConfiguration.CLASSPATH);
				String paramsStr = e.attributeValue(ServiceConfiguration.PARAMNAMES);
				String paramTypesStr = e.attributeValue(ServiceConfiguration.PARAMTYPES);
				String respType = e.attributeValue(ServiceConfiguration.RESPTYPE);
				String[] params = ObjectUtil.isNullOrEmpty(paramsStr) ? new String[0] : paramsStr.split(",");
				String[] paramTypes = ObjectUtil.isNullOrEmpty(paramTypesStr) ? new String[0] : paramTypesStr.split(",");
				ServiceConfiguration serviceConfiguration = new ServiceConfiguration(name, classPath, params, getParamTypes(paramTypes), respType);
				services.add(serviceConfiguration);
			}
		}
		loadService(services);
	}
	
	public static void loadService(Class<?> ...classes) {
		if(classes == null) return;
		List<ServiceConfiguration> services = new ArrayList<>();
		for(Class<?> c :classes) {
			String classPath = c.getName();
			Method[] ms = c.getDeclaredMethods();
			for(Method method: ms) {
				if(chechMethod(method, c.getSimpleName())) {
					String name = c.getSimpleName() + "/" + method.getName();
					String[] paramNames = getParamNames(method.getParameters());
					Class<?>[] paramTypes = getParamTypes(method.getParameterTypes());
					String respType = getRespBody(method);
					ServiceConfiguration serviceConfiguration = new ServiceConfiguration(name, classPath, paramNames, paramTypes, respType);
					services.add(serviceConfiguration);
				}
			}
		}
		loadService(services);
	}
	
	private static String[] getParamNames(Parameter... params) {
		if(params == null) return new String[0];
		String[] paramNames = new String[params.length];
		for(int i =0; i < params.length; i++) {
			paramNames[i] = params[i].getName();
		}
		return paramNames;
	}
	
	private static Class<?>[] getParamTypes(Class<?>... paramTypes) {
		if(paramTypes == null) return new Class<?>[0];
		return paramTypes;
	}
	
	private static Class<?>[] getParamTypes(String[] paramTypePaths) throws ClassNotFoundException {
		if(paramTypePaths == null) return new Class<?>[0];
		Class<?>[] classes = new Class<?>[paramTypePaths.length];
		for(int i =0; i<paramTypePaths.length; i++) {
			classes[i] = Class.forName(paramTypePaths[i]);
		}
		return classes;
	}
	
	private static boolean chechMethod(Method method, String classSimpName) {
		String methodName = method.getName();
		if("main".equals(methodName)) return false;
		String text = method.toString();
		if(!regex("^public", text) || regex("\\s+static\\s+", text)) return false;
		return true;
	}
	
	private  static  boolean regex(String regex, String text) {
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(text);
		return matcher.find();
	}
	
	private static String getRespBody(Method method) {
		Annotation[] annotations = method.getAnnotations();
		if(annotations == null || annotations.length==0) return ContentType.JSON;
		for(Annotation  annotation : annotations) {
			Class<? extends Annotation> annotationType = annotation.annotationType();
			if(RespType.class == annotationType) {
				RespType respType = (RespType) annotation;
				return respType.value();
			}
		}
		return ContentType.JSON;
	}
	
	
	private static final String ROOT_NAME = "services";
}
